package org.je.json;

import org.je.ufo.Int;
import org.je.util.CharUtil;
import org.je.util.StringUtil;

/**
 * @author fyf
 */
public class JsonParser
{
	/**
	 * @param json
	 * @return json
	 */
	public static Json parseJson(String json) {
		return (Json) parse(json);
	}

	/**
	 * @param json
	 * @return JsonArray
	 */
	public static JsonArray parseArray(String json) {
		return (JsonArray) parse(json);
	}

	/***
	 * @param json
	 * @return json or jsonArray
	 */
	public static Object parse(String json) {
		if (json == null)
			throw new NullPointerException("arrgument-is-null");
		Int off = new Int();
		char tem = InnerUtil.point2nonblank(json, off);
		if (tem == '{')
			return nextJson(json, off);
		else if (tem == '[')
			return nextArray(json, off);
		else {
			throw new RuntimeException("illegal-start-char[" + tem + "]\r\n" + ES(json, off.val()));
		}
	}

	// private
	private static Json nextJson(String source, Int off) {
		off.increase();

		if (off.val() == source.length())
			throw new RuntimeException("json-finished-error.\r\n" + ES(source, off.val()));
		Json json = new Json();
		while (true) {
			char tem = InnerUtil.point2nonblank(source, off);
			if (tem == '}') {
				off.increase();
				return json;
			}
			Object[] kv = nextKV(source, off);
			json.add((String) kv[0], kv[1]);
			tem = InnerUtil.point2nonblank(source, off);
			if (tem == ',') {
				off.increase();
			} else if (tem == '}') {
				off.increase();
				return json;
			} else
				throw new RuntimeException("illegal-char-after-KV:[" + tem + "]\r\n" + ES(source, off.val()));
		}
	}

	private static JsonArray nextArray(String source, Int off) {
		off.increase();
		if (off.val() == source.length())
			throw new RuntimeException("error-finished-at-parse-array\r\n" + ES(source, off.val()));
		JsonArray arr = new JsonArray();
		while (true) {
			char tem = InnerUtil.point2nonblank(source, off);
			if (tem == ']') {
				off.increase();
				return arr;
			}
			Object obj = getValue(source, off);
			arr.add(obj);
			tem = InnerUtil.point2nonblank(source, off);
			if (tem == ',') {
				off.increase();
			} else if (tem == ']') {
				off.increase();
				return arr;
			} else {
				throw new RuntimeException("illegal-char-at-parse-array[" + tem + "]\r\n" + ES(source, off.val()));
			}
		}
	}

	// nextKV 之后off.val() 指向之后元素起点
	private static Object[] nextKV(String source, Int off) {
		Object[] kv = new Object[2];
		kv[0] = getKey(source, off);
		char tem = InnerUtil.point2nonblank(source, off);
		if (tem != ':')
			throw new RuntimeException("parse-KV-cant-find:[:]\r\n" + ES(source, off.val()));
		off.increase();
		kv[1] = getValue(source, off);
		return kv;
	}

	private static String getKey(String source, Int off) {
		char tem = source.charAt(off.val());
		if (tem == '\'' || tem == '\"')
			return InnerUtil.getString(source, off);
		return InnerUtil.getName(source, off);
	}

	private static Object getValue(String source, Int off) {
		char s = InnerUtil.point2nonblank(source, off);
		if (s == '{')
			return nextJson(source, off);
		if (s == '[')
			return nextArray(source, off);
		if (s == '\'' || s == '\"')
			return InnerUtil.getString(source, off);
		if (s == 't') {
			if (StringUtil.match_not_check(source, "true", off.val())) {
				// off.val() += 4;// ok
				off.add(4);
				return "true";
			}
			throw new RuntimeException("parse-value-error:\r\n" + ES(source, off.val()));
		}
		if (s == 'f') {
			if (StringUtil.match_not_check(source, "false", off.val())) {
				// off.val() += 5; // ok
				off.add(5);
				return "false";
			}
			throw new RuntimeException("parse-value-error:\r\n" + ES(source, off.val()));
		}
		if (s == 'n') {
			if (StringUtil.match_not_check(source, "null", off.val())) {
				// off.val() += 4; // ok
				off.add(4);
				return "null";
			}
			throw new RuntimeException("parse-value-error:\r\n" + ES(source, off.val()));
		}
		if (s == '-' || CharUtil.isNum(s))
			return InnerUtil.getNum(source, off);
		throw new RuntimeException("illegal-value-start-char\r\n" + ES(source, off.val()));
	}

	static String ES(String source, int off) {
		if (off <= 0) {
			return source;
		}
		return source.substring(0, off) + "\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n" + source.substring(off);
	}
}
